很多开发者认为 DOM 是真的很难(或者很慢)以至于你需要一个巨大的框架才能让它服帖。于是他们投入了大量的时间去学习这些框架。一两年过去了,又一个框架火了于是你不得不扔掉之前的框架从头学起。这样的事情多重复几次就能让你对 JavaScript 产生疲劳,更不用说那多的数不过来的依赖。
如果我告诉你 DOM 其实没那么复杂,你信吗?
DOM 并不难并且它也不慢。
创建元素
要创建一个元素,你只需要写 document.createElement(tagName)
const h1 = document.createElement('h1')
// <h1></h1>
修改文本内容
HTML 元素如果没有任何内容,那就是空的,让我们用 element.textContent
来增加一些文本
h1.textContent = 'Hello world!'
// <h1>Hello world!</h1>
属性
要定义一个 HTML 元素的属性,你可以用 element.setAttribute(name, value)
h1.setAttribute('class', 'hello')
// <h1 class="hello">Hello world!</h1>
若要管理 class 有一个 element.className
属性
h1.className = 'hello'
// <h1 class="hello">Hello world!</h1>
但是,最好的方式是使用 classList
h1.classList.add('hello')
// <h1 class="hello">Hello world!</h1>
h1.classList.remove('hello')
// <h1>Hello world!</h1>
要设置一个元素的 ID,你可以使用标签属性或者 id 属性
h1.setAttribute('id', 'hello-world')
h1.id = 'hello-world'
// <h1 id="hello-world" class="hello">Hello world!</h1>
如果你不确定用标签属性(attributes)还是对象属性(properties),就用标签属性 (表单元素的状态除外,如 value 和 checked)
注意,有些布尔值的修改不能使用 element.setAttribute(someBoolean, false)
,下面这些则可以
input.checked = true
// <input checked>
input.checked = false
// <input>
input.setAttribute(‘checked’, ‘’)
// <input checked>
input.removeAttribute('checked')
// <input>
元素附加
HTML 是结构化的。可以通过 parent.appendChild(child)
来实现元素附加
document.body.appendChild(h1)
// <body><h1>Hello world!</h1></body>
元素移除
有时候你希望去掉一个元素,那么你可以使用 parent.removeChild(child)
document.body.removeChild(h1)
// <body></body>
元素查找
你可以使用下列方法来查找子元素
document.getElementById(id)
element.childNodes[i]
element.firstChild === element.childNodes[0]
element.lastChild === element.childNodes[element.childNodes.length - 1]
element.getElementsByTagName(tagName)
element.getElementsByClassName(className)
element.querySelector(query)
element.querySelectorAll(query)
注意 getElementsByTagName, getElementsByClassName 和 querySelectorAll 返回的不是数组,而是 NodeList,你不能通过 ES5 的数组快速访问方式来迭代。
元素间插入元素
想要将元素查到另一个元素的前面?试试 parent.insertBefore(child, before)
/*
* <body>
* <script src="main.js"></script>
* </body>
*/
document.body.insertBefore(h1, document.body.firstChild)
/* <body>
* <h1>Hello world!</h1>
* <script src="main.js"></script>
* </body>
*/
创建元素列表
如果我们有一些数据,可以很容易的动态创建元素。
const data = [
[ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 7, 8, 9 ]
]
const table = document.createElement('table')
data.forEach(row => {
const tr = document.createElement('tr')
row.forEach(cell => {
const td = document.createElement('td')
td.textContent = cell
tr.appendChild(td)
})
table.appendChild(tr)
})
document.body.appendChild(table)
https://jsfiddle.net/pakastin...
更新元素列表
你希望元素保持最新状态,可以这么做
const table = document.createElement('table')
document.body.appendChild(table)
updateTable(table, [
[ 1, 2 ],
[ 3, 4, 5 ],
[ 6, 7, 8, 9 ]
])
setTimeout(() => {
updateTable(table, [
[ 1, 2, 3, 4 ],
[ 5, 6, 7 ],
[ 8, 9 ]
])
}, 1000)
function updateTable (table, data) {
const rowLookup = table._lookup || (table._lookup = [])
setChildren(table, updateRows(rowLookup, data))
}
function updateRows (rowLookup, rows) {
return rows.map((row, y) => {
const tr = rowLookup[y] || (rowLookup[y] = document.createElement('tr'))
const cellLookup = tr._lookup || (tr._lookup = [])
setChildren(tr, updateCells(cellLookup, row))
return tr
})
}
function updateCells (cellLookup, cells) {
return cells.map((cell, x) => {
const td = cellLookup[x] || (cellLookup[x] = document.createElement('td'))
td.textContent = cell
return td
})
}
function setChildren (parent, children) {
let traverse = parent.firstChild
for (let i = 0; i < children.length; i++) {
const child = children[i]
if (child == null) {
return
}
if (child === traverse) {
traverse = traverse.nextSibling
} else if (traverse) {
parent.insertBefore(child, traverse)
} else {
parent.appendChild(child)
}
}
while (traverse) {
const next = traverse.nextSibling
parent.removeChild(traverse)
traverse = next
}
}
https://jsfiddle.net/pakastin...
以上代码有两个事情发生
一个隐藏属性 element._lookup = [] 用来查找子元素,使用lookup 我们可以复用DOM中已经存在的元素并更新它们。
setChildren(parent, children) 方法能让你提供一个元素列表。它很聪明的和已经附加在 parent的元素进行比较并按需进行 插入 移除 重排等操作
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。